#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<unistd.h>
#include<pthread.h>
#include"LockGuard.hpp"

using namespace std;

//全局范围内对锁进行初始化
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;

#define NUM 4

//加了锁就属于临界资源
int tickets=1000;//用多线程模拟抢票过程

class threadData
{
public:
    threadData(int number,pthread_mutex_t *mutex)
    {
        threadname="thread-"+to_string(number);
        lock=mutex;//加锁的本质是使用时间换安全
    }    

public:
    string threadname;
    pthread_mutex_t *lock;//定义的一把锁
};

#if 0
void *getTicket(void *args)
{
    threadData *td=static_cast<threadData*>(args);
    const char*name=td->threadname.c_str();
    while(true)
    {
        //进行加锁操作
        //下面加锁到解锁的这段代码就是属于我们的临界区
        //申请锁成功了之后，才可以向后面访问
        pthread_mutex_lock(td->lock);
        if(tickets>0)//多线程并发访问，进行抢票操作
        {
            usleep(1000);
            printf("who=%s,get a ticket:%d\n",name,tickets);
            tickets--;
            //tickets--的操作过程
            //1.先将tickets读入到CPU的寄存器中
            //2.CPU内部进行操作
            //3.将计算结果写回内存

            //临界区中线程可以被切换吗？->可以
            //在线程被切出去的时候，是持有锁走的，我不在的时候，照样没有人可以进入临界区访问资源
            //加锁的时候，临界区的代码越少越好

            //进行解锁操作
            pthread_mutex_unlock(td->lock);

            //锁本身就是共享资源
            //所以申请锁和释放锁本身就被设计为原子性操作
        }
        else 
        {
            //进行解锁操作
            pthread_mutex_unlock(td->lock);
            break;
        }
        //会发现抢票出问题，抢票为了负数
        //tickets是共享数据-->数据不一致问题！-->和多线程并发访问有关
        //对一个全局变量进行多线程并发++/--操作是否安全?
        //肯定是不安全的
        
        //并发操作时，导致原数据被修改
        //怎么解决？互斥 对代码进行加锁

        usleep(13);
        //为什么不加usleep，就会有线程一直在抢票
        //因为线程对于锁的竞争能力可能会不同
        //其实多线程还要模拟抢票之后的操作，使用usleep模拟

        //纯互斥环境，如果锁的分配不合理，容易导致其他线程的饥饿问题
        //适合互斥的环境就使用这个

        //为了让获取资源有一定的顺序，我们把这个叫做同步
    }
    printf("%s...quit\n",name);
    return nullptr;

    /*
    int cnt=5;
    while(cnt)
    {
        printf("%s is running...,cnt: %d\n",td->threadname.c_str()
        ,cnt--);
        sleep(1);
    }
    */
}
#endif 

void *getTicket(void *args)
{
    threadData *td=static_cast<threadData*>(args);
    const char*name=td->threadname.c_str();
    while(true)
    {
        //RAII  智能指针
        //pthread_mutex_lock(td->lock);
        LockGuard Lockguard(&lock);//临时的LockGuard对象
        if(tickets>0)
        {
            usleep(1000);
            printf("who=%s,get a ticket:%d\n",name,tickets);
            tickets--;
            //pthread_mutex_unlock(td->lock);
        }
        else 
        {
            //pthread_mutex_unlock(td->lock);
            break;
        }
        usleep(13);
    }
    printf("%s...quit\n",name);
    return nullptr;
}

int main()
{
    //进行加锁操作
    pthread_mutex_t lock;
    pthread_mutex_init(&lock,nullptr);//锁的初始化

    vector<pthread_t> tids;//保存所有线程的tid
    vector<threadData*> thread_datas;//保存所有线程的数据
    for(int i=1;i<=NUM;i++)
    {
        pthread_t tid;
        threadData* td=new threadData(i,&lock);//每一个线程都可以拿到锁
        thread_datas.push_back(td);
        pthread_create(&tid,nullptr,getTicket,thread_datas[i-1]);
        tids.push_back(tid);
    }

    for(auto thread : tids)//释放所有的线程
    {
        pthread_join(thread,nullptr);
    }
    for(auto td:thread_datas)//释放所有的线程数据
    {
        delete td;
    }

    pthread_mutex_destroy(&lock);//锁的析构
    return 0;
}